#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define BUF_SIZE_MAX 4

struct proc_attr {
//	pthread_t tid1;
//	pthread_t tid[     2];
	pthread_mutex_t buf_lock;
	pthread_cond_t not_empty;
	pthread_cond_t not_full;
};

struct proc_attr env;
int buf[BUF_SIZE_MAX]; //actually store items number is BUF_SIZE_MAX - 1
int buf_head;
int buf_rear;

static int create_detached_thread(void *(*fn)(void *arg), void *arg);
static void dump_buffer(void);

void *pwrite(void *arg)		
{
	static val = 10;

	printf("[1]run write loop ...\n");
	while(1) {
		pthread_mutex_lock(&env.buf_lock);

		if ((buf_rear - buf_head + BUF_SIZE_MAX) % BUF_SIZE_MAX == 1) {
//		if (buf_head == buf_rear) {
			printf("[1]waiting signal to set...\n");
			pthread_cond_wait(&env.not_full, &env.buf_lock);
			printf("[1]get signal\n");
		}
	
		buf[buf_head] = val++ % 100;
		printf("[1] put val is %d\n", buf[buf_head]);
		buf_head = (buf_head + 1) % BUF_SIZE_MAX;

		pthread_cond_signal(&env.not_empty);
		dump_buffer();
		pthread_mutex_unlock(&env.buf_lock);
		
		sleep(random() % 3);
	}
}

void *pread(void *arg)
{

	printf("     [2]run read loop ...\n");
	while(1) {
		pthread_mutex_lock(&env.buf_lock);

		if (buf_head == buf_rear) {
			printf("     [2]waiting signal to get...\n");
			pthread_cond_wait(&env.not_empty, &env.buf_lock);
			printf("     [2]send signal\n");
		}

		printf("     [2] get val is %d\n", buf[buf_rear]);
		buf[buf_rear] = 0;
		buf_rear = (buf_rear + 1) % BUF_SIZE_MAX;

		pthread_cond_signal(&env.not_full);
		dump_buffer();
		pthread_mutex_unlock(&env.buf_lock);
		sleep(random() % 3);
	}
}

int main()
{

	memset(buf, 0, sizeof(buf));
	buf_head = buf_rear = 0;

	pthread_mutex_init(&env.buf_lock, NULL);
	pthread_cond_init(&env.not_full, NULL);
	pthread_cond_init(&env.not_empty, NULL);

	create_detached_thread(pwrite, 0);
	create_detached_thread(pread, 0);

	while(1)
		sleep(10);
}

static int create_detached_thread(void *(*fn)(void *arg), void *arg)
{
	int ret;
	pthread_t tid;
	pthread_attr_t attr;

	ret = pthread_attr_init(&attr);
	if (ret != 0)
		return ret;

	ret = pthread_attr_setdetachstate(&attr, 
			PTHREAD_CREATE_DETACHED);
	if (ret == 0)
		ret = pthread_create(&tid, &attr, fn, arg);

	pthread_attr_destroy(&attr);
	return ret;
}

static void dump_buffer(void)
{
	int i;

	printf("BUFFER: ");
	for (i = 0; i < BUF_SIZE_MAX; i++)
		printf("%d ", buf[i]);
	printf("\n");
}
